#include <iostream>
#include "scene/scene.h"
#include "tracer/raytracer.h"
#include "tracer/cuda_tracer.h"
#include "utility/image.h"
#include "utility/hptimer.h"
#include "utility/cmdline.h"
#include "gui.h"

struct Settings
{
	rtString	scene;
	int			width;
	int			height;
	int			traceDepth;
	rtString	output;
	int			threads;
	int			guiMode;
};

bool ReadSettings(int argc, char** argv, Settings& settings);



int main(int argc, char** argv)
{
	Settings settings;
	if (!ReadSettings(argc, argv, settings))
		return 0;

	HPTimer timer;

	std::cout << "Loading scene..." << std::endl;
	Scene scene;
	scene.LoadFromFile(settings.scene);

	if (!settings.guiMode)
	{
		std::cout << "Creating virtual screen..." << std::endl;
		Image* screen = Image::CreateFromMemory(NULL, settings.width, settings.height, 32);

		std::cout << "Starting tracing..." << std::endl;
		timer.Start();
		if (true)
		{
			RayTracer tracer;
			tracer.SetMaximumTraceDepth(settings.traceDepth);
			{
				if (settings.threads > 0)
					tracer.RenderMP(scene, *screen, settings.threads);
				else
					tracer.Render(scene, *screen);
			}
		}
		else
		{
			CudaTracer tracer;
			tracer.PrepareSceneData(scene, *screen);
			tracer.Render(*screen);
		}
		timer.Stop();

		std::cout << "Scene rendered in " << timer.GetTimeInSeconds() << " seconds" << std::endl;
		std::cout << "Generated image saved to data/out_R.tga" << std::endl;

		Image::WriteToFile(settings.output, screen);
		delete screen;
	}
	else
	{
		std::cout << "Creating GUI..." << std::endl;
		GUI::Instance().Create(settings.width, settings.height, "cuTraycer");
		GUI::Instance().Run(scene);
	}

	return 0;
}



bool ReadSettings(int argc, char** argv, Settings& settings)
{
	CommandLine cmdLine;
	if (!cmdLine.ParseArgv(argc - 1, argv + 1))
	{
		std::cout << std::endl << "\tPlease supply a parameter." << std::endl << std::endl;
		CommandLine::PrintUsage();
		return false;
	}

	int idx;

	// iOrange - read scene path
	if (-1 != (idx = cmdLine.GetParameterIdx(CMD_PARAM_SCENE)))
		settings.scene = cmdLine.GetParameterData(idx);
	else
	{
		std::cout << "\tPlease provide scene file." << std::endl;
		return false;
	}

	// iOrange - read screen width
	if (-1 != (idx = cmdLine.GetParameterIdx(CMD_PARAM_WIDTH)))
		settings.width = atoi(cmdLine.GetParameterData(idx).c_str());
	else
	{
		std::cout << "\tPlease provide screen width." << std::endl;
		return false;
	}

	// iOrange - read screen height
	if (-1 != (idx = cmdLine.GetParameterIdx(CMD_PARAM_HEIGHT)))
		settings.height = atoi(cmdLine.GetParameterData(idx).c_str());
	else
	{
		std::cout << "\tPlease provide screen height." << std::endl;
		return false;
	}

	if (-1 != (idx = cmdLine.GetParameterIdx(CMD_PARAM_TRACEDEPTH)))
		settings.traceDepth = atoi(cmdLine.GetParameterData(idx).c_str());
	else
	{
		settings.traceDepth = s_MaxTraceDepth;
		std::cout << "\tMaximum trace depth not provided. Default is " << s_MaxTraceDepth << std::endl;
	}

	// iOrange - read output path
	if (-1 != (idx = cmdLine.GetParameterIdx(CMD_PARAM_OUTPUT)))
		settings.output = cmdLine.GetParameterData(idx);
	else
	{
		std::cout << "\tPlease provide output path." << std::endl;
		return false;
	}

	// iOrange - read output path
	settings.threads = 0;
	if (-1 != (idx = cmdLine.GetParameterIdx(CMD_PARAM_THREADS)))
		settings.threads = atoi(cmdLine.GetParameterData(idx).c_str());

	if (settings.threads < 2)
		settings.threads = 0;

	settings.guiMode = (-1 != cmdLine.GetParameterIdx(CMD_PARAM_GUIMODE));

	return true;
}
